#include "list.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* Here we are going to write some functions to support a linked list that stores Students (first name, last name, grade, gtid)  */
/* Here is the data the user in this case us will be storing into our linked list */
/* YOU ARE REQUIRED TO COMPLETE THIS PART */
typedef struct Student
{
	char* first_name; /* This will be malloc'd!*/
	char* last_name; /* This will also be malloc'd */
	int grade;
	unsigned int gtid;
} Student;

Student* create_student(const char* first_name, const char* last_name, int grade, unsigned int gtid)
{
	/* First allocate a student on the heap */
	Student* student = malloc(sizeof(Student));

	/* Allocate enough space for the first and last names */
	student->first_name = malloc(strlen(first_name)+1);
	student->last_name = malloc(strlen(last_name)+1);
	/* AND copy the first and last name to the first and last name fields in the struct */
	if(student->first_name == NULL || student->last_name == NULL) return NULL;
	strncpy(student->first_name, first_name, strlen(first_name) + 1);
	strncpy(student->last_name, last_name, strlen(first_name) + 1);
	/* Set the grade and gtid */
	student->grade = grade;
	student->gtid = gtid;
	/* Notice there are two functions that will help you with the second and third steps */
	/* strlen and strncpy will be helpful */
	/* type man strlen and man strncpy to learn how to use them */

	/* DO NOT store the parameters first_name/last_name in the struct you allocate */ 
	return student;
}

/* For these functions below you should never be passed in NULL */
/* You will always get valid data here */
void print_student(void* data)
{
	/* TODO Implement just print out all of the information here. */
	/* Your output should exactly look like this without no weird characters printing out.
	Student: FIRSTNAME LASTNAME
	Grade: GRADE
	GtID: GTID
	*/
	Student *student = (Student*) data;
	printf("\nStudent: %s %s", (student->first_name), (student->last_name));
	printf("\nGrade: %d", student->grade);
	printf("\nGTID: %d", student->gtid);
	/* printf and the format specifiers %d %s will be useful here 
	Again use the man pages if you don't know how to use printf */
}

void free_student(void* data)
{
	/* TODO Implement You are to first free any fields that were malloc'd. */
	/* Then free the struct in this order */
	Student *student = (Student*) data;
	free(student->first_name);
	free(student->last_name);
	free(student);
}

int student_eq(const void* a, const void* b)
{
	/* TODO **TEST if two students are equal */
	/* Two students are equal if all of their fields are equal */
	/* To compare strings the function strncmp will be useful */
	/* Again you can read the man pages for strncmp */
	Student* student_a = (Student*) a;
	Student* student_b = (Student*)	b;
	int compare_first_name = !strcmp(student_a->first_name, student_b->first_name);
	int compare_last_name = !strcmp(student_a->last_name, student_b->last_name);

	return compare_first_name & compare_last_name & (student_a->grade == student_b->grade) & (student_a->gtid == student_b->gtid);
}

/*
 * return true if the student's first_name is "man", false otherwise.
 */
int student_pred(const void* a)
{
	Student* student_a = (Student*) a;
	char name[10] = "man";
	return (!strcmp(student_a->first_name,name));
}

/* This main function does a little **TESTing
   Like all good CS Majors you are required to **TEST
   your code here. There is no substitute for **TESTing
   and you should be sure to **TEST for all edge cases
   e.g., calling remove_front on an empty list.
*/
int main(void) 
{
	/* Now to make use of all of this stuff */
	list* llist = create_list();
  
  	/* What does an empty list contain?  Lets use our handy traversal function */
  	printf("**TEST CASE 1\nAn Empty list should print nothing here:");
  	traverse(llist, print_student);
	printf("\n");

 	/* Lets add a student and then print */
 	push_front(llist, create_student("Nick", "Polive", 56, 239402128));
 	printf("**TEST CASE 2\nA List with one student should print that student:");
 	traverse(llist, print_student);
 	printf("\n");
 	
 	/* Lets remove that student and then print */
 	remove_front(llist, free_student);
 	printf("**TEST CASE 3\nAnother Empty list should print nothing here:");
 	traverse(llist, print_student);
 	printf("\n");

 	/* Lets add two elements and then print */
 	push_front(llist, create_student("Baron", "von Baris", 92, 123456789));
 	push_front(llist, create_student("George", "Burdell", 65, 902313373));
 	printf("**TEST CASE 4\nA List with two students should print those two students:");
 	traverse(llist, print_student);
 	printf("\n"); 	

 	/* **TEST find occurence when the node is not found */
	// Notice the commented line below is BAD because the return value from create_student was malloc'd and you never free it!
	// find_occurence(llist, create_student("Nurse", "Joy", 3, 131313137), student_eq));
	Student* p = create_student("Nurse", "Joy", 3, 131313137);
	printf("**TEST CASE 5\nFinding occurence when a node is not in the list should fail: ");
	printf("%d\n", find_occurrence(llist, p, student_eq));
	/* Remember to FREE everything you malloc I malloc'd space for p and now I am done with it so I free it */
	free_student(p);

  	/* Lets kill the list */
  	empty_list(llist, free_student);
 	printf("**TEST CASE 6\nAfter freeing all nodes the list should be empty:");
 	traverse(llist, print_student);
 	printf("\n"); 	
 	
 	/* YOU ARE REQUIRED TO MAKE MORE **TEST CASES THAN THE ONES PROVIDED HERE */
 	/* You will get points off if you do not you should at least TEST each function here */
 	

 	/* push front and back multiple students*/
	printf("**TEST CASE 7\nA List with 4 students should print those two students: \n");
 	push_front(llist, create_student("Baron", "von Baris", 92, 123456789));
	push_front(llist, create_student("George", "Burdell", 65, 902313373));
	push_front(llist, create_student("Tam", "Vo", 60, 00000));
	push_back(llist, create_student("Bao", "Tran", 00, 10));
	traverse(llist, print_student);
	printf("\n");

	/* empty it again */
	printf("**TEST CASE 8\nAfter freeing all nodes the list should be empty:\n");
	remove_back(llist,free_student);
	remove_front(llist,free_student);
	remove_back(llist,free_student);
	remove_front(llist,free_student);
	remove_front(llist,free_student);
	traverse(llist, print_student);
	printf("is_empty should return true %d\n", is_empty(llist));
	printf("get_index(0) should return NULL %p\n", get_index(llist,0));
	printf("\n");

	/* push back once with empty list */
	printf("**TEST CASE 9\nA List with 1 students should print those two students: \n");
	push_back(llist, create_student("Bao", "Tran", 00, 10));
	traverse(llist, print_student);
	printf("is_empty should return false %d", is_empty(llist));
	printf("\nA List with 2 students should print those two students: \n");
	push_back(llist, create_student("Tam", "Vo", 60, 10));
	traverse(llist, print_student);
	printf("\n");

	/* return front*/
	printf("**TEST CASE 10");
	printf("\nShould print out the first student \"Bao Tran\":\n");
	print_student((Student*)front(llist));
	printf("\n");

	printf("**TEST CASE 11");
	printf("\nget_index should make it print out 4 different students\n");
	push_front(llist, create_student("Baron", "von Baris", 92, 123456789));
	push_front(llist, create_student("George", "Burdell", 65, 902313373));
	for(int i = 0; i < size(llist); i++)
	{
		print_student((Student*)get_index(llist,i));
	}
	printf("\n");

	printf("\n**TEST CASE 12");
	printf("\nfind occurrence should return true: %d\n",
	find_occurrence(llist, create_student("George", "Burdell", 65, 902313373), student_eq));
	printf("\n");

	printf("\n**TEST CASE 13");
	push_back(llist, create_student("George", "Burdell", 65, 902313373));
	push_back(llist, create_student("abc", "Burdell", 65, 902313373));
	traverse(llist, print_student);
	printf("\nremove George should return 1 and make it print out the rest: ");
	printf("\n %d", remove_data(llist,create_student("George", "Burdell", 65, 902313373), student_eq,free_student));
	traverse(llist, print_student);
	printf("\n");

	printf("\n**TEST CASE 14");
	printf("\nremove Bao Tran should return 1 and make it print out the rest: Baron, Tam, abc ");
	printf("\n %d", remove_data(llist,create_student("Bao", "Tran", 0, 10), student_eq,free_student));
	traverse(llist, print_student);
	printf("\n");

	printf("\n**TEST CASE 15");
	printf("\nremove_index 1 should make it print out Baron and abc");
	remove_index(llist,1, free_student);
	traverse(llist, print_student);
	push_front(llist, create_student("a","b",1,2));
	printf("\n\nremove_index 0 should make it print out Baron and abc" );
	remove_index(llist,0, free_student);
	traverse(llist, print_student);
	push_front(llist, create_student("d","b",1,2));

	printf("\n\nremove_index 2 should make it print out d and Baron");
	remove_index(llist,2, free_student);
	traverse(llist, print_student);
	push_front(llist, create_student("m","b",1,2));
	push_front(llist, create_student("man","b",1,2));
	push_back(llist, create_student("man","b",1,2));
	printf("\n");

	printf("\n**TEST CASE 16");
	printf("\nremove_if equal man. Should print out m, d, and Baron");
	remove_if(llist,student_pred,free_student);
	traverse(llist, print_student);
	printf("\n");

	printf("\n**TEST CASE 17");
	printf("\nback should return Baron");
	print_student(back(llist));
	printf("\n");


 	/* **TESTing over clean up*/
 	free(llist);
 	
  	return 0;
}
